package com.panopset.gui.container;

import com.panopset.gui.LookAndFeel;

import java.awt.LayoutManager;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;

import com.panopset.AbstractApplication;
import com.panopset.centralen.resource.PersistentContainer;
import com.panopset.gui.TPanel;

/**
 * Pan field.
 *
 * Fields should do the following:
 * <ul>
 * <li>
 * Have static access to font, color, other attributes that will be common to an
 * application.</li>
 * <li>
 * Persist with a document, or application.</li>
 * <li>
 * Allow for validation. (text, number, file, directory).</li>
 * </ul>
 *
 * @author Karl Dinwiddie
 *
 * @param <T>
 *            T extends JComponent.
 */
public abstract class PanField<T extends JComponent> extends
        PersistentContainer {

    @Override
    protected final ValueMirror createValueMirror() {
        return new PersistentContainer.ValueMirror() {

            @Override
            public void setValue(final String newValue) {
                setComponentValue(newValue);
            }

            @Override
            public String getValue() {
                return getComponentValue();
            }
        };
    }

    @Override
    protected abstract boolean isValid(final String valueToCheck);

    /**
     * Tool tip text.
     */
    private final String ptoolTipText;

    /**
     * Layout.
     */
    private LayoutManager playout;

    /**
     * @return Component value.
     */
    protected abstract String getComponentValue();

    /**
     * Set component value.
     *
     * @param value
     *            Value to set.
     */
    protected abstract void setComponentValue(String value);

    /**
     * @param borderTitle
     *            Border title.
     * @param toolTipText
     *            Tool tip text.
     * @param defaultValue
     *            Default value.
     * @param layout
     *            Layout.
     */
    public PanField(final String borderTitle, final String toolTipText,
            final String defaultValue, final LayoutManager layout) {
        this(borderTitle, toolTipText, defaultValue);
        playout = layout;
    }

    /**
     * @param toolTipText
     *            Tool tip text.
     * @param defaultValue
     *            Default value.
     * @param layout
     *            Layout.
     */
    public PanField(final String toolTipText, final String defaultValue,
            final LayoutManager layout) {
        this(null, toolTipText, defaultValue);
        playout = layout;
    }

    /**
     * @param borderTitle
     *            Border title.
     * @param toolTipText
     *            Tool tip text.
     * @param defaultValue
     *            Default value.
     */
    public PanField(final String borderTitle, final String toolTipText,
            final String defaultValue) {
        super(defaultValue);
        pborderTitle = borderTitle;
        ptoolTipText = toolTipText;
        AbstractApplication.add(this);
    }

    /**
     * @return Inner component.
     */
    protected abstract T createComponent();

    /**
     * @return Layout.
     */
    protected abstract LayoutManager createLayout();

    /**
     * Containing panel.
     */
    private TPanel panel;

    /**
     * @return Containing panel.
     */
    public final JPanel getPanel() {
        if (panel == null) {
            panel = new TPanel.Builder().layout(getLayout()).construct();
            panel.setBackground(LookAndFeel.Colors.BG_COLOR.getColor());
            panel.setToolTipText(ptoolTipText);
            panel.add(getInnerComponent());
            setNormalHighlighting();
        }
        return panel;
    }

    /**
     * Inner component gui.
     */
    private T gui;

    /**
     * @return Layout.
     */
    final LayoutManager getLayout() {
        if (playout == null) {
            playout = createLayout();
        }
        return playout;
    }

    /**
     * @return inner component.
     */
    public final T getInnerComponent() {
        if (gui == null) {
            gui = createComponent();
            gui.setToolTipText(ptoolTipText);
        }
        return gui;
    }

    /**
     * Border title.
     */
    private String pborderTitle;

    /**
     * @return Border title.
     */
    public final String getBorderTitle() {
        if (pborderTitle == null) {
            pborderTitle = "";
        }
        return pborderTitle;
    }

    /**
     * Normal border.
     */
    private TitledBorder normalBorder;

    /**
     * Wait border.
     */
    private TitledBorder waitBorder;

    /**
     * Alert border.
     */
    private TitledBorder alertBorder;

    /**
     * In progress border.
     */
    private TitledBorder inprogressBorder;

    /**
     * Set border title.
     *
     * @param newBorderTitle
     *            New border title.
     */
    public final void setBorderTitle(final String newBorderTitle) {
        pborderTitle = newBorderTitle;
        getNormalBorder().setTitle(pborderTitle);
        getWaitBorder().setTitle(pborderTitle);
        getAlertBorder().setTitle(pborderTitle);
        getInprogressBorder().setTitle(pborderTitle);
    }

    /**
     * Check to see if we need to alert the user for input action.
     */
    protected void checkInput() { }

    /**
     * Is waiting for input.
     */
    private boolean isWaitingForInput = false;

    /**
     * @return True if waiting for input.
     */
    private boolean isWaitingForInput() {
        checkInput();
        return isWaitingForInput;
    }

    /**
     * @param value Value to set.
     */
    public final void setWaitingForInput(final boolean value) {
        isWaitingForInput = value;
    }

    /**
     * @return Normal titled border.
     */
    protected final TitledBorder getNormalBorder() {
        if (isWaitingForInput()) {
            return getAlertBorder();
        }
        if (normalBorder == null) {
            LineBorder lb = new LineBorder(
                    LookAndFeel.Colors.BORDER_COLOR_NORMAL.getColor(), 1);
            normalBorder = new TitledBorder(lb, pborderTitle);
            normalBorder.setTitleFont(LookAndFeel.Fonts.TITLE_FONT_NORMAL
                    .getFont());
            normalBorder.setTitleColor(LookAndFeel.Colors.BORDER_COLOR_NORMAL
                    .getColor());
        }
        return normalBorder;
    }

    /**
     * @return Wait titled border.
     */
    protected final TitledBorder getWaitBorder() {
        if (waitBorder == null) {
            LineBorder lb = new LineBorder(LookAndFeel.Colors.BORDER_COLOR_WAIT
                    .getColor(), 1);
            waitBorder = new TitledBorder(lb, pborderTitle);
            waitBorder.setTitleColor(LookAndFeel.Colors.BORDER_COLOR_WAIT
                    .getColor());
            waitBorder.setTitleFont(LookAndFeel.Fonts.TITLE_FONT_NORMAL
                    .getFont());
        }
        return waitBorder;
    }

    /**
     * @return Alert titled border.
     */
    protected final TitledBorder getAlertBorder() {
        if (alertBorder == null) {
            LineBorder lb = new LineBorder(
                    LookAndFeel.Colors.BORDER_COLOR_ALERT.getColor(), 1);
            alertBorder = new TitledBorder(lb, pborderTitle);
            alertBorder.setTitleColor(LookAndFeel.Colors.BORDER_COLOR_ALERT
                    .getColor());
            alertBorder.setTitleFont(LookAndFeel.Fonts.TITLE_FONT_NORMAL
                    .getFont());
            setNormalHighlighting();
        }
        return alertBorder;
    }

    /**
     * @return In progress titled border.
     */
    protected final TitledBorder getInprogressBorder() {
        if (inprogressBorder == null) {
            LineBorder lb = new LineBorder(
                    LookAndFeel.Colors.BORDER_COLOR_INPROGRESS.getColor(), 1);
            inprogressBorder = new TitledBorder(lb, pborderTitle);
            inprogressBorder
                    .setTitleColor(LookAndFeel.Colors.BORDER_COLOR_INPROGRESS
                            .getColor());
            inprogressBorder.setTitleFont(LookAndFeel.Fonts.TITLE_FONT_NORMAL
                    .getFont());
            setNormalHighlighting();
        }
        return inprogressBorder;
    }

    /**
     * Set normal highlighting for the boder title.
     */
    public final void setNormalHighlighting() {
        if (pborderTitle == null) {
            return;
        }
        getPanel().setBorder(getNormalBorder());
        reset();
    }

    /**
     * If you want to show something in progress, use this.
     */
    public final void setWaitHighlighting() {
        if (pborderTitle == null) {
            return;
        }
        getPanel().setBorder(getWaitBorder());
        reset();
    }

    /**
     * Set alert highlighting.
     */
    public final void setAlertHighlighting() {
        if (pborderTitle == null) {
            return;
        }
        getPanel().setBorder(getAlertBorder());
        reset();
    }

    /**
     * Set in progress highlighting.
     */
    public final void setInprogressHighlighting() {
        if (pborderTitle == null) {
            return;
        }
        getPanel().setBorder(getInprogressBorder());
        reset();
    }

    /**
     * Reset.
     */
    public final void reset() {
        getPanel().invalidate();
    }
}
